package persistencia;
 
import java.util.*;
import javax.jdo.PersistenceManager;
import javax.jdo.PersistenceManagerFactory;
import javax.jdo.Transaction;
import javax.jdo.Extent;
import javax.jdo.Query;
import server.*;

import utils.*;
import comun.MyException;
import comun.MensajeDeError;

import persistencia.dominio.ClaseDominio; 
/**  
 * <p>Title: ABM Generico</p>
 * <p>Description: </p>
 * <p>Copyright: Copyright (c) 2003</p>
 * <p>Company: Yo</p>
 * @author Yo
 * 
 *     
 * @version 1.0
 */


public class Persistencia {
  boolean debug = false;
  PersistenceManagerFactory pmf;
  PersistenceManager pm;
  Query query;    
  /** Transaccion, se debe inicializar */
  com.triactive.jdo.Transaction tx;
  static private Hashtable hashtableSubclase = null;
  int aislamiento = java.sql.Connection.TRANSACTION_SERIALIZABLE;


  public Persistencia() {
     Singleton.cantidadBeginTransaction++;

     this.pmf = Singleton.getPmf()  ;
     this.pm = pmf.getPersistenceManager();
   // este cambioo lo hice para reutilizar el pm porque nome libera la coneccion a la base de datos
//     this.pm = pmf.getPersistenceManager();
     if (hashtableSubclase==null)  // para inicializala una sola vez
       hashtableSubclase= this.hashTableSubClases();

     //if (debug)   System.out.println("************************************* ");
     //if (debug)   System.out.println("************************************* ");
     //if (debug)   System.out.println("Singleton.cantidadBeginTransaction: "+Singleton.cantidadBeginTransaction);
     //if (debug)   System.out.println("Singleton.cantidadCommitTransaction: "+Singleton.cantidadCommitTransaction);
     //if (debug)   System.out.println("************************************* ");
     //if (debug)   System.out.println("************************************* ");

  }

  /**
   * Carga una tabla de hash con classes y sus respectivas subclasses
   * @return
   */
  private Hashtable hashTableSubClases()
  {
    Hashtable hashtableSubClases= new Hashtable();
    hashtableSubClases.put("persistencia.dominio.CuentaCorreo",
                           new String[]{"persistencia.dominio.ListaCorreo","persistencia.dominio.CuentaCorreo"});
    hashtableSubClases.put("persistencia.dominio.Respuesta",
                           new String[]{"persistencia.dominio.RespuestaSinOpcion","persistencia.dominio.RespuestaConOpcion"});
    hashtableSubClases.put("persistencia.dominio.PropiedadPagina",
                           new String[]{"persistencia.dominio.PaginaStilo","persistencia.dominio.EncabezadoPagina","persistencia.dominio.ColorFondo"});
    hashtableSubClases.put("persistencia.dominio.AulaCompuesta",
                           new String[]{"persistencia.dominio.Facultad","persistencia.dominio.Organizacion","persistencia.dominio.Curso"});
    hashtableSubClases.put("persistencia.dominio.Aula",
                           new String[]{"persistencia.dominio.Comision","persistencia.dominio.AulaCompuesta"});
    //borre comisi�n porque est� abajo, por grupos
    hashtableSubClases.put("persistencia.dominio.Curso",
                           new String[]{});
    hashtableSubClases.put("persistencia.dominio.Organizacion",
                           new String[]{});
    hashtableSubClases.put("persistencia.dominio.Facultad",
                           new String[]{});
    /*CODIGO AGREGADO POR GRUPO CONDE-PEREYRA - TEMA: FAQ*/
    hashtableSubClases.put("persistencia.dominio.RespuestaFrecuenteFAQGPC",
                           new String[]{});
    hashtableSubClases.put("persistencia.dominio.PreguntaFrecuenteFAQGPC",
            			   new String[]{});
    /*HASTA ACA*/
    /*CODIGO AGREGADO POR GRUPO CONDE-PEREYRA - TEMA: GRUPOS*/
    hashtableSubClases.put("persistencia.dominio.Texto",new String[]{"persistencia.dominio.Invitacion",
    																 "persistencia.dominio.Solicitud",
    																 //TEMA: FAQ
    																 "persistencia.dominio.PreguntaFrecuenteFAQGPC",
    																 "persistencia.dominio.RespuestaFrecuenteFAQGPC"});
    hashtableSubClases.put("persistencia.dominio.Invitacion",new String[]{});
    hashtableSubClases.put("persistencia.dominio.Solicitud",new String[]{});
    hashtableSubClases.put("persistencia.dominio.Grupo",new String[]{});
    hashtableSubClases.put("persistencia.dominio.Comision",
                           new String[]{"persistencia.dominio.Grupo"});
    /*HASTA ACA*/
    hashtableSubClases.put("persistencia.dominio.Carpeta",
                           new String[]{"persistencia.dominio.Software","persistencia.dominio.Material","persistencia.dominio.MaterialAdicional","persistencia.dominio.MiMaterial"});
    hashtableSubClases.put("persistencia.dominio.Software",
                           new String[]{});
    hashtableSubClases.put("persistencia.dominio.Material",
                           new String[]{});
    hashtableSubClases.put("persistencia.dominio.MiMaterial",
            new String[]{});
    hashtableSubClases.put("persistencia.dominio.Link",
                           new String[]{"persistencia.dominio.Biblioteca","persistencia.dominio.Enlace"});
    return hashtableSubClases;
  }


  /**
   * Hace persistente un nuevo objeto de dominio
   * @param o
   */
  public void hacerPersistente(Object o){
    pm.makePersistent(o);
  }

  public void deletePersistente(Object o){
    pm.deletePersistent(o);
  }

  public void deletePersistente(Set set){
    for (Iterator i = set.iterator(); i.hasNext(); ) {
      Object item = i.next();
      pm.deletePersistent(item);
    }
  }

  public void deletePersistente(Vector vector){
    Enumeration enum = vector.elements();
    while (enum.hasMoreElements()) {
      Object item = enum.nextElement();
      pm.deletePersistent(item);
    }
  }


  /** Crear la transaccion */
  public void crearTransaccion(){
    try {

      tx = (com.triactive.jdo.Transaction)pm.currentTransaction();
      tx.setTransactionIsolation(aislamiento);
      tx.begin();
    }
    catch (Exception ex) {
      ex.printStackTrace();
      //System.out.println("****************************************************");
      //System.out.println("************ EXPLOTO LA BASE DE DATOS **************");
      //System.out.println("****************************************************");
      //System.out.println("****************************************************");
      //System.out.println("************ EXPLOTO LA BASE DE DATOS **************");
      //System.out.println("****************************************************");
      //System.out.println("****************************************************");
      //System.out.println("************ EXPLOTO LA BASE DE DATOS **************");
      //System.out.println("****************************************************");
      //System.out.println("****************************************************");
      //System.out.println("************ EXPLOTO LA BASE DE DATOS **************");
      //System.out.println("****************************************************");
      //System.out.println("****************************************************");
      //System.out.println("************ EXPLOTO LA BASE DE DATOS **************");
      //System.out.println("****************************************************");
      Singleton.reiniciarBaseDatos();
      this.pmf = Singleton.getPmf();
      this.pm = pmf.getPersistenceManager();
      tx = (com.triactive.jdo.Transaction)pm.currentTransaction();
      tx.setTransactionIsolation(aislamiento);
      tx.begin();

    }
  }

  /** El commit */
  public void commit(){
    Singleton.cantidadCommitTransaction++;

    //System.out.println("~~~~~~~~~ ANTES COMMIT");
    if (tx.isActive())
      tx.commit();
    //System.out.println("~~~~~~~~~ DESPUES COMMIT");
    if (!pm.isClosed())
      pm.close();


  }

  /** El Roolback */
  public void rollback(){
    //System.out.println("~~~~~~~~~ ANTES rollback");
    if (tx.isActive())
       tx.rollback();
    //System.out.println("~~~~~~~~~ DESPUES rollback");

    if (!pm.isClosed())
    pm.close();
  }


  /**
   * Retorna si el id pertenece a algun objecto persistente del vector
   * @param id
   * @param vector
   * @return
   */
  public boolean contiene (Long id, Set set) {
    ClaseDominio claseDominio;
    for (Iterator i = set.iterator(); i.hasNext() ; ) {
      claseDominio = (ClaseDominio)i.next();
      if (id.compareTo(claseDominio.getId())==0)
        return true;
    }
    return false;
  }

  /**
   * Retorna si el id pertenece a algun objecto persistente del vector
   * @param id
   * @param vector
   * @return
   */
  public boolean contiene (Long id, Vector vector) {
    ClaseDominio claseDominio;
    Enumeration enum = vector.elements();
    while (enum.hasMoreElements()) {
      claseDominio = (ClaseDominio)enum.nextElement();
      if (id.compareTo(claseDominio.getId())==0)
        return true;
    }
    return false;
    }



  /**
   * elimina un objeto persistente de un vector por su id
   * @param objetoDominio
   * @param vector
   */
  public void eliminarObjetoDeUnVectorPorId(Object objetoDominio,Set set)  {
    ClaseDominio claseDominio=null;
    Long idObjeto= ((ClaseDominio)objetoDominio).getId();

    for (Iterator i = set.iterator();i.hasNext(); ) {
      claseDominio = (ClaseDominio) i.next();
      if (claseDominio.getId().compareTo(idObjeto)==0)
        set.remove(claseDominio);
    }
  }

  /**
   *
   * retorna si dos objetos persistentes tienen el mismo id
   * @param objetoPersistente1
   * @param objetoPersistente2
   * @return
   */
  public boolean iguales(Object objetoPersistente1, Object objetoPersistente2)
  {
      // Controla si algunos de los objetos es null
      if (objetoPersistente1==null && objetoPersistente2!=null)
        return false;

      if (objetoPersistente1!=null && objetoPersistente2==null)
        return false;

      if (objetoPersistente1==null && objetoPersistente2==null)
        return false;

      // obtiene los id de los objetos a comparar
      Long id1 = ((ClaseDominio)objetoPersistente1).getId();
      Long id2 = ((ClaseDominio)objetoPersistente2).getId();

      return  (id1.longValue()==id2.longValue());
  }

  /**
   * Obtiene un objeto persistente a partir de su Id y class
   * @param clase
   * @param id
   * @return
   */
  public Object getObjectoPorIdSinSubClases(String nombreClase, Long id) throws MyException
  {
    try {
      // Crear el objeto query y todo lo necesario
      java.lang.Class clase = Class.forName(nombreClase);
      Extent clnObjeto = pm.getExtent(clase, false);
      String filter = new String("id == ido");
      Query query = pm.newQuery(clnObjeto,filter);
      // Declara �parametros poara el query
      String parametro = "Long ido";
      query.declareParameters(parametro);

      // Ejecuta el query
      Collection objetos= (Collection)query.execute(id);

      // Obtiene el objeto con el id buscado
      if (!objetos.isEmpty())
      {   Iterator i = objetos.iterator();
          return i.next();
      }
      query.closeAll();
    }
    catch (Exception ex) {
      ex.printStackTrace();
      throw (new MyException(MensajeDeError.OBJETO_NO_ENCONTRADO));
    }
    //si no hay ningun objeto con ese id retorna null
    return null;
  }


  /**
   * Retorna el objeto serializable conrrespondiente al objeto de persistente con id id
   * @param nombreClaseSerializable
   * @param id
   * @return
   */
  public Object getObjectoPorId(String nombreClase, Long id) throws MyException
   {
    try
    {
      //if (debug)   System.out.println("NombreCLase:"+nombreClase);
      //if (debug)   System.out.println("Id:"+id);
      if (id == null || id.longValue() == 0)
        return null;

      String[] subClases;
      Object objeto = getObjectoPorIdSinSubClases(nombreClase, id);
      if (objeto!= null)
        return objeto;
      else
        // si tengo el ID debe existir una clase con ese dato, entonces
        // busco en los sub tipos, esto sirve para la recursion en la herencia

        // para obtener las subClases necesito un objeto
        subClases = (String[]) hashtableSubclase.get(nombreClase);
      //if (debug)   System.out.println("----->Clase:"+nombreClase+" Id:"+id);
      // ciclo dentro de las sub clases a lo ancho
      for (int i = 0; i < subClases.length; i++) {
        String subClase = subClases[i];
        try {
          //if (debug)   System.out.println("subClase:"+subClase+" Id:"+id);
//          objeto = this.getObjectoPorIdSinSubClases(subClase, id);
          objeto = this.getObjectoPorId(subClase, id);
          if (objeto!=null)
              return objeto;
        }
        catch (MyException ex) {
          ex.printStackTrace();
          // no hacer nada, seguir ciclando entre las clase hija;
        }
      }
      return null;
  /*    // hacer la recursion en los hijos
      for (int i = 0; i < subClases.length; i++) {
        String subClase = subClases[i];
        try {

          objeto = this.getObjectoPorIdSinSubClases(subClase, id);
          if (objeto!=null)
              return objeto;
        }
        catch (MyException ex) {
          ex.printStackTrace();
          // no hacer nada, seguir ciclando entre las clase hija;
        }
      }
      throw (new MyException(MensajeDeError.ERROR_BASE_DE_DATOS));
   */
   }
    catch (Exception ex) {
      ex.printStackTrace();
      throw (new MyException(MensajeDeError.ERROR_BASE_DE_DATOS));
    }
  }

  // retorna si existe el objeto o no, utiliza el metodo
  // getObjetoPorId, si no salta la excepcion(existe) retorna true
  public boolean existeObjetoPorId(String nombreClase, Long id)
  {
    try {
      // si no existe genera excepcion de nul pointer
      getObjectoPorId(nombreClase,id).toString();
      return true;
    }
    catch (Exception ex) {
      return false;
    }
  }



  /**
   * Obtiene todos los objetos de una clase
   * @param nombreClase
   * @return
   * @throws MyException
   */
  public Vector getObjectosPorClase(String nombreClase) throws MyException
  {
    // Utiliza  getObjectosPorClase con atributo a Ordenar vacio
     return getObjectosPorClase(nombreClase,"","","",null,null,null,null);
  }


// trae solamente los objetos de la clase y no de las subclases
  public Vector getObjectosPorClase(String nombreClase,String atributoAOrdenar, String filtro,String vars, String parametros,
                                    Object parametro1,Object parametro2,Object parametro3 ) throws MyException
  {
    return   getObjectosPorClase(nombreClase,atributoAOrdenar, filtro,vars, parametros,
                                    parametro1, parametro2, parametro3, false );

  }
  /**
   * Obtiene todos los objetos de una clase ordenados  por atributoAOrdenar
   * @param nombreClase
   * @param atributoAOrdenar
   * @return
   * @throws MyException
   */
  public Vector getObjectosPorClase(String nombreClase,String atributoAOrdenar, String filtro,String vars, String parametros,
                                    Object parametro1,Object parametro2,Object parametro3, boolean extend ) throws MyException
  {
    Collection objetos = null;
    Vector objetosRetorno=new Vector();
    try {
      // Crear el objeto query y todo lo necesario
      java.lang.Class clase = Class.forName(nombreClase);
      Extent clnObjeto = pm.getExtent(clase, extend);
      String filter = filtro;
      Query query= null;
      query = pm.newQuery(clnObjeto,filter);
      if (vars!=null)
        query.declareVariables(vars);
      if (parametros!=null)
        query.declareParameters(parametros);

      String orden="";
      // la verdad es que no me acuerdo prque hago esto
      // me acorde, es por si viene descending despues del atributo a ordenar
      if (atributoAOrdenar.lastIndexOf(" ")!=-1)
      {
        orden = atributoAOrdenar.substring(atributoAOrdenar.lastIndexOf(" "));
        atributoAOrdenar = atributoAOrdenar.substring(0,atributoAOrdenar.lastIndexOf(" "));
      }

      //if (debug)   System.out.println("OOOOOOOOOOOORDEN: "+orden);
      //if (debug)   System.out.println("AAAAAAAAAAATRRIBUTO A ORDENAR: "+atributoAOrdenar);
      //if (debug)   System.out.println("COMPARACION: "+(orden.indexOf("descending")==-1));


      if (atributoAOrdenar.compareTo("")!=0)
      {
        // no es descendiente
        if (orden.indexOf("descending")==-1)
          query.setOrdering(atributoAOrdenar+" ascending");
        else
          query.setOrdering(atributoAOrdenar+" descending");
      }
      else
        query.setOrdering(atributoAOrdenar);


      // Ejecuta el query dependiendo de los parametros
      if (parametro1==null)
        objetos= (Collection)query.execute();
      else
      {
        if (parametro2==null)
          objetos= (Collection)query.execute(parametro1);
        else
        {
          if (parametro3==null)
            objetos= (Collection)query.execute(parametro1,parametro2);
          else
            objetos= (Collection)query.execute(parametro1,parametro2,parametro3);
        }
      }

      objetosRetorno.addAll(objetos);
      query.closeAll();
    }
    catch (Exception ex) {
      ex.printStackTrace();
      throw (new MyException(MensajeDeError.ERROR_BASE_DE_DATOS));
    }
    return objetosRetorno;
  }



  /**
   * Obtiene todos los objetos de una clase que cumplen con filtro
   * @param clase
   * @return
   */
  public Vector getObjectosPorClaseYFiltro(String nombreClase,String filtro) throws MyException
  {
    return this.getObjectosPorClase(nombreClase,"",filtro,"",null,null,null,null);
  }

  /**
   * Obtiene todos los objetos de una clase que cumplen con filtro
   * y ordenados por atributoAOrdenar
   * @param clase
   * @return
   */
  public Vector getObjectosPorClaseYFiltro(String nombreClase,String atributoAOrdenar,String filtro) throws MyException
  {
    return this.getObjectosPorClase(nombreClase,atributoAOrdenar,filtro,"",null,null,null,null);
  }


  public Vector getObjectosPorClaseYFiltro(String nombreClase,String atributoAOrdenar,String filtro, boolean extend) throws MyException
  {
    return this.getObjectosPorClase(nombreClase,atributoAOrdenar,filtro,"",null,null,null,null,extend);
  }


  public Vector getObjectosPorClaseYFiltro(String nombreClase,String atributoAOrdenar,String filtro,String vars) throws MyException
  {
    return this.getObjectosPorClase(nombreClase,atributoAOrdenar,filtro,vars,null,null,null,null);
  }

  /**
   * Obtiene un objeto persistente a partir de su Id y class
   * @param clase
   * @param ids   lista de id de los objetos a recuperar
   * @return
   */
  public Vector getObjectosPorIds(String nombreClase, List ids) throws MyException
  {
    Collection objetos = null;
    Vector retornObjetos= new Vector();
    try {
      // Crear el objeto query y todo lo necesario
      java.lang.Class clase = Class.forName(nombreClase);
      Extent clnObjeto = pm.getExtent(clase, false);
      String filter = new String("ids.contain(id)");
      Query query = pm.newQuery(clnObjeto,filter);
      // Declara �parametros poara el query
      String parametro = "Collection ids";
      query.declareParameters(parametro);

      // Ejecuta el query
      objetos= (Collection)query.execute(ids);
      retornObjetos.addAll(objetos);
      query.closeAll();
    }
    catch (Exception ex) {
      ex.printStackTrace();
      throw (new MyException(MensajeDeError.ERROR_BASE_DE_DATOS));
    }
    return retornObjetos;
  }

  /**
   * Obtiene todos los objetos de una clase Utilizando un SQL
   * SE DEBE CARRAR CON getObjectosPorSQLClose
   * @param sqlText
   * @param nombreClase
   * @param imports
   * @param atributoAOrdenar
   * @param parametros
   * @param parametro1
   * @param parametro2
   * @param parametro3
   * @return
   * @throws MyException
   */
  public Vector getObjectosPorSQLOpen(String sqlText,Class nombreClase,String imports, String parametros,
                                    Object parametro1,Object parametro2,Object parametro3) throws MyException
  {
    Vector retorno = new Vector();
    java.util.Collection objetos;
    try
    {
     query = pm.newQuery("javax.jdo.query.TJDOSQL", sqlText);
     query.setClass(nombreClase);

     if (imports!=null)
       query.declareImports(imports);

     if (parametros!=null)
       query.declareParameters(parametros);

     // Ejecuta el query dependiendo de los parametros
     if (parametro1==null)
       objetos= (Collection)query.execute();
     else
     {
       if (parametro2==null)
         objetos= (Collection)query.execute(parametro1);
       else
       {
         if (parametro3==null)
           objetos= (Collection)query.execute(parametro1,parametro2);
         else
           objetos= (Collection)query.execute(parametro1,parametro2,parametro3);
       }
     }

     retorno.addAll(objetos);
    }
    catch (Exception ex) {
      ex.printStackTrace();
      throw (new MyException(MensajeDeError.ERROR_BASE_DE_DATOS));
    }
   return retorno;
  }

  public void getObjectosPorSQLClose()
  {
    this.query.closeAll();
  }

  public void setAislamiento(int ailamiento){
     this.aislamiento = ailamiento;
     //tx.setTransactionIsolation(aislamiento);
  }
  
  public void setSerializable(){
     this.aislamiento = java.sql.Connection.TRANSACTION_SERIALIZABLE;
     tx.setTransactionIsolation(aislamiento);
  }

  public void setRepeatableRead(){
     this.aislamiento = java.sql.Connection.TRANSACTION_REPEATABLE_READ;
     tx.setTransactionIsolation(aislamiento);
  }

  public void setReadCommitted(){
     this.aislamiento = java.sql.Connection.TRANSACTION_READ_COMMITTED;
     tx.setTransactionIsolation(aislamiento);
  }

  public void setReadUncommitted(){
     this.aislamiento = java.sql.Connection.TRANSACTION_READ_UNCOMMITTED;
     tx.setTransactionIsolation(aislamiento);
  }
}